package at.ac.univie.ise.generator;

import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.ResourceBundle;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jooq.impl.Factory;

import de.pyranja.util.DateUtils;
import de.pyranja.util.Utils;

import at.ac.univie.ise.common.Config;
import at.ac.univie.ise.common.Constants;
import at.ac.univie.ise.schema.Tables;
import at.ac.univie.ise.schema.tables.records.CarRecord;
import at.ac.univie.ise.schema.tables.records.CategoryRecord;
import at.ac.univie.ise.schema.tables.records.ModelRecord;
import at.ac.univie.ise.schema.tables.records.OffersRecord;
import at.ac.univie.ise.schema.tables.records.StationRecord;

public class VehicleGenerator {
	
	// logger
	private final Log log = LogFactory.getLog(VehicleGenerator.class);
	
	private final Factory create;
	private final Random rng;
	
	private List<CategoryRecord> categories;
	private List<ModelRecord> models;
	private List<CarRecord> cars;
	
	private int plateCounter;
	
	public VehicleGenerator(Factory f) {
		log.info(">> generating vehicles");
		create = f;
		rng = Config.getInstance().getRng();
		plateCounter = Utils.rangedRandomInt(10, 200, rng);
		String bundleBase = Config.getInstance().getBundleBase();
		ResourceBundle catBundle = ResourceBundle.getBundle(bundleBase + ".category");
		ResourceBundle modelBundle = ResourceBundle.getBundle(bundleBase + ".model");
		categories = loadCategories(catBundle);
		associateCategoriesAndStations();
		models = loadModels(modelBundle);		
		cars = generateCars();
		log.info("<< vehicles generated");
	}
	
	private List<CategoryRecord> loadCategories(ResourceBundle rb) {
		log.debug(">> loading categories");
		List<CategoryRecord> result = new LinkedList<>();
		CategoryRecord temp = null;
		Map<Integer,String[]> cats = new HashMap<>();
		for (Enumeration<String> keys = rb.getKeys(); keys.hasMoreElements(); ) {
			String currKey = keys.nextElement();
			String[] values = rb.getString(currKey).split(" ");
			cats.put(Integer.parseInt(currKey), values);
		}
		for (int i = 0; i < cats.size(); i++) {
			String[] values = cats.get(i);
			temp = create.newRecord(Tables.CATEGORY);
			temp.setCatname(values[0]);
			temp.setDescription(values[0]);
			temp.setMinage(Integer.parseInt(values[2]));
			temp.setPrice(Integer.parseInt(values[3]));
			temp.setUpgrade(values[1].equalsIgnoreCase("none") ? null : values[1]);
			temp.store();
			temp.refresh();
			if ( log.isTraceEnabled() ) { log.trace("-- loaded from bundle : "+ temp.toString()); }
			result.add(temp);
		}
		log.info("<< "+ result.size() +" categories loaded");
		return result;
	}
	
	private List<ModelRecord> loadModels(ResourceBundle rb) {
		log.debug(">> loading models");
		List<ModelRecord> result = new LinkedList<>();
		ModelRecord temp = null;
		for (Enumeration<String> keys = rb.getKeys(); keys.hasMoreElements(); ) {
			String currKey = keys.nextElement();
			String[] values = rb.getString(currKey).split(" ");
			temp = create.newRecord(Tables.MODEL);
			temp.setModelid(Integer.parseInt(currKey));
			temp.setModelname(values[1]);
			temp.setCatname(values[3]);
			temp.setManufacturer(values[0]);
			temp.setReqlicence(values[2]);
			temp.store();
			temp.refresh();
			if ( log.isTraceEnabled() ) { log.trace("-- loaded from bundle : "+ temp.toString()); }
			result.add(temp);
		}
		log.info("<< "+ result.size() +" models loaded");
		return result;
	}
	
	private void associateCategoriesAndStations() {
		List<StationRecord> stations = create.fetch(Tables.STATION);
		for (StationRecord currStation : stations) {
			Collections.shuffle(categories, rng);
			for (CategoryRecord currCat : categories) {
				OffersRecord temp = create.newRecord(Tables.OFFERS);
				temp.setCatname(currCat.getCatname());
				temp.setStationid(currStation.getStationid());
				temp.store();
				if ( log.isTraceEnabled() ) { log.trace("-- associated "+ currStation.toString() +" with "+ currCat.toString()); }
				if (rng.nextBoolean()) {
					break;
				}
			}
		}
	}
	
	private List<CarRecord> generateCars() {
		log.debug(">> generating cars");
		List<CarRecord> result = new LinkedList<>();
		CarRecord temp = null;
		List<StationRecord> stations = create.fetch(Tables.STATION);
		for (StationRecord currStation : stations) {
			for (int i = 0; i < Config.getInstance().getCarScale(); i++) {
				temp = create.newRecord(Tables.CAR);
				temp.setPlate(generatePlate());
				temp.setStationid(currStation.getStationid());
				temp.setModelid(Utils.randomElem(models, rng).getModelid());
				temp.setKm(Utils.rangedRandomInt(2000, 90000, rng));
				temp.setAdmission(DateUtils.toSQLDate(DateUtils.randomAgoDateByYear(rng, 1, 3)));
				temp.store();
				if ( log.isTraceEnabled() ) { log.trace("-- generated "+ temp.toString()); }
				result.add(temp);
			}
		}
		log.info("<< "+ result.size() +" cars generated");
		return result;
	}
	
	private String generatePlate() {
		StringBuilder sb = new StringBuilder();
		sb.append((char)(rng.nextInt(26) + 'A'));
		sb.append((char)(rng.nextInt(26) + 'A'));
		sb.append("-");
		sb.append(plateCounter % 100000);
		plateCounter += Utils.rangedRandomInt(1, Constants.PLATE_STEP, rng);
		return sb.toString();
	}

	/**
	 * @return the categories
	 */
	public List<CategoryRecord> getCategories() {
		return categories;
	}

	/**
	 * @return the models
	 */
	public List<ModelRecord> getModels() {
		return models;
	}

	/**
	 * @return the cars
	 */
	public List<CarRecord> getCars() {
		return cars;
	}
}
